package cn.yx.common.core.util.http.httpclient;

import cn.yx.common.core.exception.CommonException;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
 * <p>Http Get请求类</p>
 * <p>封装了部分可直接调用的get方法，如果没有合适的get方法，则可以调用buildGet方法获得HttpGet对象后，再调用sendGet方法。</p>
 *
 * @author Wgssmart
 */
@Slf4j
public class HttpGetUtil extends HttpBasic {

    /**
     * HTTP GET请求访问指定的url
     *
     * @param url url,包括查询字符串
     * @return 指定url所返回的字符串数据
     */
    public static String get(String url) {
        return sendGet(buildHttpGet(url, null), STRING_RESPONSE_HANDLER);
    }

    /**
     * HTTP GET请求访问指定的url
     *
     * @param url     url,包括查询字符串
     * @param headers headers头部数据
     * @return 指定url所返回的字符串数据
     */
    public static String get(String url, Map<String, String> headers) {
        return sendGet(buildHttpGet(url, headers), STRING_RESPONSE_HANDLER);
    }

    /**
     * HTTP GET请求访问指定的url。可通过getCookies方法获取返回的cookie
     *
     * @param url          url,包括查询字符串
     * @param headers      headers头部数据
     * @param cookieList   提交的Cookie集合
     * @param returnCookie 是否从服务端获取cookie
     * @return 指定url所返回的字符串数据
     */
    public static String get(String url, Map<String, String> headers, List<Cookie> cookieList, boolean returnCookie) {
        return sendGet(buildHttpGet(url, headers), cookieList, returnCookie, STRING_RESPONSE_HANDLER);
    }

    /**
     * HTTP GET请求访问指定的url。可通过getCookies方法获取返回的cookie
     *
     * @param url          url,包括查询字符串
     * @param cookieList   提交的Cookie集合
     * @param returnCookie 是否从服务端返回cookie
     * @return 指定url所返回的字符串数据
     */
    public static String get(String url, List<Cookie> cookieList, boolean returnCookie) {
        return sendGet(buildHttpGet(url, null), cookieList, returnCookie, STRING_RESPONSE_HANDLER);
    }

    /**
     * 发送HTTP GET请求，并且需要指定相应的ResponseHandler，同时还可以指定数字证书与协议
     *
     * @param url          url,包括查询字符串
     * @param certPath     证书路径
     * @param certPassword 证书密码
     * @param certType     证书类型
     * @param sslProtocols ssl协议数组
     * @return 通过在ResponseHandler中指定的具体类型来返回响应数据
     */
    public static String get(String url, String certPath, String certPassword, String certType, String[] sslProtocols) {
        return sendGet(buildHttpGet(url, null), certPath, certPassword, certType, sslProtocols, STRING_RESPONSE_HANDLER);
    }

    /**
     * 创建HttpGet对象，可指定Headers
     *
     * @param url
     * @param headers
     * @return
     */
    public static HttpGet buildHttpGet(String url, Map<String, String> headers) {
        HttpGet httpGet = new HttpGet(url);
        setHeaders(httpGet, headers);
        return httpGet;
    }

    /**
     * 发送HTTP GET请求，并且需要指定相应的ResponseHandler
     *
     * @param httpGet         HttpGet请求
     * @param responseHandler ResponseHandler响应处理对象
     * @param <T>             响应处理对象中可通过泛型来指定返回的具体数据类型
     * @return 通过在ResponseHandler中指定的具体类型来返回响应数据
     */
    public static <T> T sendGet(HttpGet httpGet, ResponseHandler<T> responseHandler) {
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            return executeGet(httpClient, httpGet, responseHandler);
        } catch (IOException e) {
            log.error("http client execute get error: {}", e.getMessage());
            // @SimplifiedChinese=HTTP GET请求失败
            // @English=Http Get error
            throw new CommonException("cn.yx.common.core.executeHttpGetError", e);
        }
    }

    /**
     * 发送HTTP GET请求，并且需要指定相应的ResponseHandler
     *
     * @param httpGet         HttpGet请求
     * @param cookieList      需要提交到服务端的cookie数据
     * @param returnCookie    是否需要从服务端获取cookie数据
     * @param responseHandler ResponseHandler响应处理对象
     * @param <T>             响应处理对象中可通过泛型来指定返回的具体数据类型
     * @return 通过在ResponseHandler中指定的具体类型来返回响应数据，并获取由服务端返回的cookie数据到cookies成员变量中
     */
    public static <T> T sendGet(HttpGet httpGet, List<Cookie> cookieList, boolean returnCookie, ResponseHandler<T> responseHandler) {
        CookieStore cookieStore = getCookieStore(cookieList);
        try (CloseableHttpClient httpClient = HttpClients.custom().setDefaultCookieStore(cookieStore).build()) {
            T t = executeGet(httpClient, httpGet, responseHandler);
            if (returnCookie) {
                setCookies(cookieStore.getCookies());
            }
            return t;
        } catch (IOException e) {
            log.error("http client execute get error: {}", e.getMessage());
            throw new CommonException("cn.yx.common.core.executeHttpGetError", e);
        }
    }

    /**
     * 发送HTTP GET请求，并且需要指定相应的ResponseHandler，同时还可以指定数字证书与协议
     *
     * @param httpGet         HttpGet请求
     * @param certPath        证书路径
     * @param certPassword    证书密码
     * @param certType        证书类型
     * @param sslProtocols    ssl协议数组
     * @param responseHandler ResponseHandler响应处理对象
     * @param <T>             响应处理对象中可通过泛型来指定返回的具体数据类型
     * @return 通过在ResponseHandler中指定的具体类型来返回响应数据
     */
    public static <T> T sendGet(HttpGet httpGet, String certPath, String certPassword,
                                String certType, String[] sslProtocols, ResponseHandler<T> responseHandler) {
        try (CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(
                HttpBasic.getSslConnectionSocketFactory(certPath, certPassword, certType, sslProtocols)).build()) {
            return executeGet(httpClient, httpGet, responseHandler);
        } catch (IOException e) {
            log.error("http client execute get error: {}", e.getMessage());
            throw new CommonException("cn.yx.common.core.executeHttpGetError", e);
        }
    }

    /**
     * 执行http get请求
     *
     * @param httpClient
     * @param httpGet
     * @param responseHandler
     * @param <T>
     * @return
     */
    private static <T> T executeGet(HttpClient httpClient, HttpGet httpGet, ResponseHandler<T> responseHandler) {
        try {
            httpGet.setConfig(HttpBasic.requestConfig);
            return httpClient.execute(httpGet, responseHandler);
        } catch (IOException e) {
            log.error("http client execute get error: {}", e.getMessage());
            throw new CommonException("cn.yx.common.core.executeHttpGetError", e);
        }
    }

}
